﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Windows.Input;
using ScrumFactory.Composition;
using ScrumFactory.Composition.ViewModel;
using ScrumFactory.Services;
using ScrumFactory.Windows.Helpers;
using System.Linq;

namespace ScrumFactory.Backlog.ViewModel {
    
   

    /// <summary>
    /// The Backlog View Model.
    /// </summary>
    [Export(typeof(IBacklogViewModel))]
    [Export(typeof(IPanelViewModel))]
    public class BacklogViewModel : BasePanelViewModel, IBacklogViewModel, IPanelViewModel, INotifyPropertyChanged {

        private BacklogStatusFilter statusFilter;
        private string searchFilterText;

        private string newItemName;
        private System.Windows.Data.CollectionViewSource backlogViewSource;
        private DelayAction delayFilter;
    
        private IBacklogService backlogService;
        private IBackgroundExecutor executor;
        private IEventAggregator aggregator;

        /// <summary>
        /// Initializes a new instance of the <see cref="BacklogViewModel"/> class.
        /// </summary>
        /// <param name="backlogService">The backlog service.</param>
        /// <param name="backgroundExecutor">The background executor.</param>
        /// <param name="eventAggregator">The event aggregator.</param>
        [ImportingConstructor()]
        public BacklogViewModel(
            [Import] IBacklogService backlogService,
            [Import] IBackgroundExecutor backgroundExecutor,
            [Import] IEventAggregator eventAggregator) {

            this.backlogService = backlogService;
            this.executor = backgroundExecutor;
            this.aggregator = eventAggregator;

            aggregator.Subscribe<Project>(ScrumFactoryEvent.ViewProjectDetails, LoadBacklogItems);
            aggregator.Subscribe <ICollection<Role>>(ScrumFactoryEvent.ProjectRolesLoaded, SyncPlannedHours);
            
            CreateNewBacklogItemCommand = new DelegateCommand(CanCreateNewBacklogItem, CreateNewBacklogItem);
            DeleteBacklogItemCommand = new DelegateCommand<IBacklogItemDetailViewModel>(RemoveBacklogItem);
            
            backlogViewSource = new System.Windows.Data.CollectionViewSource();
            backlogViewSource.Filter += new System.Windows.Data.FilterEventHandler(backlogViewSource_Filter);
            delayFilter = new DelayAction(1200, new DelayAction.ActionDelegate(() => { FilteredBacklog.Refresh(); }));

        }


        private void SyncPlannedHours(ICollection<Role> roles) {
            if (Project == null || Project.BacklogItems==null)
                return;
            foreach (BacklogItem item in Project.BacklogItems)
                item.SyncPlannedHoursAndRoles();
        }

        
        /// <summary>
        /// Handles the Filter event of the backlogViewSource control.
        /// Only items that matches the SearchFilter and the StatusFilter are accepted.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Data.FilterEventArgs"/> instance containing the event data.</param>
        private void backlogViewSource_Filter(object sender, System.Windows.Data.FilterEventArgs e) {
            
            BacklogItemDetailViewModel vm = e.Item as BacklogItemDetailViewModel;
            if (vm == null)
                return;

            bool statusFilter = false;
            if (StatusFilter == BacklogStatusFilter.ALL_BACKLOG)
                statusFilter = true;

            if (StatusFilter == BacklogStatusFilter.TODO_BACKLOG
                && vm.Item.Status!=(short)BacklogItemStatus.ITEM_DONE
                && vm.Item.Status != (short)BacklogItemStatus.ITEM_CANCELED)
                statusFilter = true;

            if (StatusFilter == BacklogStatusFilter.SELECTED_BACKLOG
                && vm.Item.Project.CurrentSprint!=null
                && vm.Item.SprintNumber == vm.Item.Project.CurrentSprint.SprintNumber)
                statusFilter = true;

            if (StatusFilter == BacklogStatusFilter.PLANNING_BACKLOG                
                && vm.Item.SprintNumber >= vm.Item.Project.CurrentValidSprint.SprintNumber)
                statusFilter = true;

            bool textSearchFilter = false;
            if (String.IsNullOrEmpty(SearchFilterText) || vm.Item.Name.Contains(SearchFilterText) || vm.Item.BacklogItemNumber.ToString().Equals(SearchFilterText))
                textSearchFilter = true;

            e.Accepted = statusFilter && textSearchFilter;

        }

        /// <summary>
        /// Loads the backlog items from the service.
        /// </summary>
        /// <param name="project">The project.</param>
        private void LoadBacklogItems(Project project) {
            IsLoadingData = true;
            Project = project;
            executor.StartBackgroundTask<ICollection<BacklogItem>>(
                () => {return this.backlogService.GetCurrentBacklog(Project.ProjectUId);},
                OnBacklogItemsLoaded);
        }

        /// <summary>
        /// Called when backlog items loaded.
        /// Creates the Items View Model collection and raises publishes the BacklogItemsLoaded event.
        /// </summary>
        /// <param name="items">The items.</param>
        private void OnBacklogItemsLoaded(ICollection<BacklogItem> items) {

            // resets the Model BacklogItem list
            Project.BacklogItems = new List<BacklogItem>();
            
            // create viewmodel items collection and add items to it
            Items = new ObservableCollection<IBacklogItemDetailViewModel>();
            if (items != null) {
                foreach (BacklogItem item in items)
                    AddBacklogItem(item, false);
            }
                                
            // sets the filtered view source 
            backlogViewSource.Source = Items;

            // raises the change property and the event
            OnPropertyChanged("FilteredBacklog");

            SyncPlannedHours(Project.Roles);

            // notify other modules
            aggregator.Publish<ICollection<BacklogItem>>(ScrumFactoryEvent.BacklogItemsLoaded, Project.BacklogItems);

            // load data is over
            IsLoadingData = false;
        }

        /// <summary>
        /// Adds the backlog item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void AddBacklogItem(BacklogItem item, bool isNew) {
            item.Project = Project;            
            Project.BacklogItems.Add(item);
            BacklogItemDetailViewModel vm = new BacklogItemDetailViewModel(backlogService, executor, aggregator, item);
            vm.JustHasBeenInserted = isNew;
            Items.Add(vm);            
        }

        /// <summary>
        /// Removes the backlog item form the Model, from the View Model collection and from the
        /// services.
        /// </summary>
        /// <param name="itemViewModel">The item view model to be removed.</param>
        private void RemoveBacklogItem(IBacklogItemDetailViewModel itemViewModel) {

            IEditableObjectViewModel editable = itemViewModel as IEditableObjectViewModel;
            editable.JustHasBeenRemoved = true;

            executor.StartBackgroundTask(
                    () => {
                        backlogService.DeleteBacklogItem(itemViewModel.Item.BacklogItemUId);
                        System.Threading.Thread.Sleep(300); // ugly no? this is just to give time for the remove animation take do its job
                    },
                    () => {
                        Project.BacklogItems.Remove(itemViewModel.Item);
                        Items.Remove(itemViewModel);
                    });
        }

        /// <summary>
        /// Determines whether this instance can create new backlog item.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance can create new backlog item; otherwise, <c>false</c>.
        /// </returns>
        private bool CanCreateNewBacklogItem() {
            return !String.IsNullOrEmpty(newItemName);
        }

        /// <summary>
        /// Creates the new backlog item and saves it.
        /// </summary>
        private void CreateNewBacklogItem() {

            // create the new item
            BacklogItem newItem = new BacklogItem {
                BacklogItemUId = Guid.NewGuid().ToString(),
                BacklogItemNumber = Project.NextBacklogItemNumber,
                ProjectUId = Project.ProjectUId,
                Project = Project,
                Name = this.NewItemName,
                Status = (short)BacklogItemStatus.ITEM_REQUIRED,
                CreateDate = DateTime.Now
            };
            newItem.SyncPlannedHoursAndRoles();

            // adds it
            AddBacklogItem(newItem, true);

            // clean the textbox
            NewItemName = String.Empty;
            
            // save its
            executor.StartBackgroundTask(() => { this.backlogService.AddBacklogItem(newItem); }, () => { });
        }
        
               
        #region IBacklogViewModel Members

        /// <summary>
        /// Gets the project.
        /// </summary>
        /// <value>The project.</value>
        public Project Project { private set; get; }

        /// <summary>
        /// Gets or sets the view.
        /// </summary>
        /// <value>The view.</value>
        [Import(typeof(IBacklogView))]
        public IBacklogView View { get; set; }

       

        /// <summary>
        /// Gets or sets the status filter.
        /// </summary>
        /// <value>The status filter.</value>
        public BacklogStatusFilter StatusFilter {
            get {
                return this.statusFilter;
            }
            set {
                this.statusFilter = value;
                if (FilteredBacklog != null)
                    FilteredBacklog.Refresh();
                OnPropertyChanged("StatusFilter");
            }
        }

        /// <summary>
        /// Gets or sets the search filter text.
        /// </summary>
        /// <value>The search filter text.</value>
        public string SearchFilterText {
            get {
                return this.searchFilterText;
            }
            set {
                this.searchFilterText = value;
                OnPropertyChanged("SearchFilterText");
                delayFilter.StartAction();
            }
        }        

        /// <summary>
        /// Gets the new name of the item to be created.
        /// </summary>
        /// <value>The new name of the item.</value>
        public string NewItemName {
            get {
                return this.newItemName;
            }
            set {
                this.newItemName = value;
                ((DelegateCommand)CreateNewBacklogItemCommand).NotifyCanExecuteChanged();
                OnPropertyChanged("NewItemName");
            }
        }

        /// <summary>
        /// Gets the filtered backlog.
        /// </summary>
        /// <value>The filtered backlog.</value>
        public ICollectionView FilteredBacklog {
            get {
                return backlogViewSource.View;
            }
        }

        /// <summary>
        /// Gets the backlog items.
        /// </summary>
        /// <value>The collection of View Model of each item.</value>        
        public ObservableCollection<IBacklogItemDetailViewModel> Items { get; private set; }

        /// <summary>
        /// Gets the delete backlog item command.
        /// </summary>
        /// <value>The delete backlog item command.</value>
        public DelegateCommand<IBacklogItemDetailViewModel> DeleteBacklogItemCommand { get; private set; }

        /// <summary>
        /// Gets the create new backlog item command.
        /// </summary>
        /// <value>The create new backlog item command.</value>
        public ICommand CreateNewBacklogItemCommand { get; private set; }

        #endregion

        #region IPanelViewModel Members

        /// <summary>
        /// Gets the panel placement.
        /// </summary>
        /// <value>The panel placement.</value>
        public PanelPlacements PanelPlacement {
            get { return PanelPlacements.ProjectTab; }
        }

        /// <summary>
        /// Gets the name of the panel.
        /// </summary>
        /// <value>The name of the panel.</value>
        public string PanelName {
            get {
                return Properties.Resources.ResourceManager.GetString("Backlog");
            }
        }

        /// <summary>
        /// Gets the panel Unique id.
        /// </summary>
        /// <value>The panel Unique id.</value>
        public string PanelUId {
            get {
                return "BACKLOG_TAB";
            }
        }

        /// <summary>
        /// Gets the panel display order when it is displayed at the view.
        /// </summary>
        /// <value>The panel display order.</value>
        public int PanelDisplayOrder {
            get {
                return 300;
            }
        }

        /// <summary>
        /// Gets the view.
        /// </summary>
        /// <value>The view.</value>
        object IPanelViewModel.View {
            get {
                return this.View;
            }
        }
     
        #endregion


        
    }
}
